"""
AI对话服务
负责处理与外部AI API的交互，包括消息发送、响应处理等
"""

import json
import time
import requests
import asyncio
from typing import Dict, List, Any, Optional, Tuple, AsyncGenerator, Callable
from django.utils import timezone
from django.contrib.auth import get_user_model
import aiohttp

from .models import AIConversation, AIMessage, AIProvider, AIModel, TokenUsageLog
from .context_manager import ConversationContextManager

User = get_user_model()


class AIServiceError(Exception):
    """AI服务异常"""
    pass


class AIService:
    """AI对话服务"""
    
    def __init__(self, conversation: AIConversation):
        self.conversation = conversation
        self.context_manager = ConversationContextManager(conversation)
    
    def send_message(
        self,
        content: str,
        user: User,
        include_context: bool = True,
        context_limit: int = 10,
        max_context_tokens: int = 120000,
        parent_message_id: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        发送消息并获取AI回复
        
        Args:
            content: 用户消息内容
            user: 发送消息的用户
            include_context: 是否包含上下文
            context_limit: 上下文消息数量限制
            max_context_tokens: 上下文最大Token数
            parent_message_id: 父消息ID
            
        Returns:
            包含AI回复信息的字典
        """
        start_time = time.time()
        
        try:
            # 1. 保存用户消息
            user_message = self._save_user_message(
                content=content,
                user=user,
                parent_message_id=parent_message_id
            )
            
            # 1.5. 清除上下文缓存，确保新消息被包含
            self.context_manager.clear_context_cache()
            
            # 2. 构建AI请求上下文
            if include_context:
                context_messages = self.context_manager.get_context_for_ai_request(
                    message_limit=context_limit,
                    token_limit=max_context_tokens
                )
            else:
                # 只包含系统提示词和当前消息
                context_messages = []
                if self.conversation.ai_role:
                    context_messages.append({
                        'role': 'system',
                        'content': self.conversation.ai_role.system_prompt
                    })
                context_messages.append({
                    'role': 'user',
                    'content': content
                })
            
            # 3. 调用AI API
            ai_response = self._call_ai_api(context_messages)
            
            # 4. 保存AI回复
            ai_message = self._save_ai_message(
                content=ai_response['content'],
                user=user,
                parent_message_id=user_message.id,
                metadata=ai_response.get('metadata', {})
            )
            
            # 5. 记录Token使用
            self._log_token_usage(
                user=user,
                user_message=user_message,
                ai_message=ai_message,
                token_usage=ai_response['token_usage']
            )
            
            # 6. 更新上下文缓存
            self.context_manager.add_message_to_context(user_message)
            self.context_manager.add_message_to_context(ai_message)
            
            response_time = time.time() - start_time
            
            return {
                'user_message_id': user_message.id,
                'ai_message_id': ai_message.id,
                'content': ai_response['content'],
                'token_usage': ai_response['token_usage'],
                'model_info': {
                    'model_name': self.conversation.ai_model.name,
                    'model_code': self.conversation.ai_model.model_code,
                    'provider': self.conversation.ai_model.provider.name
                },
                'response_time': response_time,
                'context_used': include_context,
                'context_token_count': sum(
                    self.context_manager._estimate_tokens(msg['content']) 
                    for msg in context_messages
                )
            }
            
        except Exception as e:
            raise AIServiceError(f"AI服务调用失败: {str(e)}")
    
    def _save_user_message(
        self,
        content: str,
        user: User,
        parent_message_id: Optional[int] = None
    ) -> AIMessage:
        """保存用户消息"""
        parent_message = None
        if parent_message_id:
            try:
                parent_message = AIMessage.objects.get(
                    id=parent_message_id,
                    conversation=self.conversation
                )
            except AIMessage.DoesNotExist:
                pass
        
        # 估算Token数量
        token_count = self.context_manager._estimate_tokens(content)
        
        message = AIMessage.objects.create(
            conversation=self.conversation,
            ai_role=None,  # 用户消息没有AI角色
            content=content,
            token_count=token_count,
            parent_message=parent_message,
            created_by=user
        )
        
        return message
    
    def _save_ai_message(
        self,
        content: str,
        user: User,
        parent_message_id: Optional[int] = None,
        metadata: Dict[str, Any] = None
    ) -> AIMessage:
        """保存AI回复消息"""
        parent_message = None
        if parent_message_id:
            try:
                parent_message = AIMessage.objects.get(
                    id=parent_message_id,
                    conversation=self.conversation
                )
            except AIMessage.DoesNotExist:
                pass
        
        # 估算Token数量
        token_count = self.context_manager._estimate_tokens(content)
        
        message = AIMessage.objects.create(
            conversation=self.conversation,
            ai_role=self.conversation.ai_role,
            content=content,
            token_count=token_count,
            metadata=metadata or {},
            parent_message=parent_message,
            created_by=user
        )
        
        return message
    
    def _call_ai_api(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
        """
        调用AI API
        
        Args:
            messages: 消息列表
            
        Returns:
            AI响应数据
        """
        if not self.conversation.ai_model:
            raise AIServiceError("未配置AI模型")
        
        provider = self.conversation.ai_model.provider
        
        # 解密API密钥
        api_key = provider.decrypt_api_key()
        
        # 根据提供商类型调用不同的API
        if provider.provider_type == 'openai':
            return self._call_openai_api(messages, provider, api_key)
        elif provider.provider_type == 'anthropic':
            return self._call_anthropic_api(messages, provider, api_key)
        else:
            return self._call_custom_api(messages, provider, api_key)
    
    def _call_openai_api(
        self,
        messages: List[Dict[str, str]],
        provider: AIProvider,
        api_key: str
    ) -> Dict[str, Any]:
        """调用OpenAI API"""
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }
        
        # 合并默认参数和模型参数
        params = provider.default_params.copy()
        params.update(self.conversation.ai_model.model_params)
        
        data = {
            'model': self.conversation.ai_model.model_code,
            'messages': messages,
            **params
        }
        
        try:
            # 处理URL拼接，避免双斜杠
            endpoint = provider.api_endpoint.rstrip('/')
            full_url = f"{endpoint}/chat/completions"
            
            # 打印请求详情到控制台
            print("\n=== AI API 请求详情 ===")
            print(f"URL: {full_url}")
            print(f"Headers: {headers}")
            print("Request Data:")
            print(json.dumps(data, indent=2, ensure_ascii=False))
            print("=" * 50)
            
            response = requests.post(
                full_url,
                headers=headers,
                json=data,
                timeout=300
            )
            
            # 打印响应详情
            print(f"\n=== AI API 响应详情 ===")
            print(f"Status Code: {response.status_code}")
            print(f"Response Headers: {dict(response.headers)}")
            print("Response JSON:")
            try:
                response_json = response.json()
                print(json.dumps(response_json, indent=2, ensure_ascii=False))
            except json.JSONDecodeError:
                print(f"非JSON响应: {response.text[:1000]}{'...' if len(response.text) > 1000 else ''}")
            print("=" * 50)
            
            response.raise_for_status()
            
            result = response.json()
            
            return {
                'content': result['choices'][0]['message']['content'],
                'token_usage': {
                    'input_tokens': result['usage']['prompt_tokens'],
                    'output_tokens': result['usage']['completion_tokens'],
                    'total_tokens': result['usage']['total_tokens']
                },
                'metadata': {
                    'model': result.get('model'),
                    'finish_reason': result['choices'][0].get('finish_reason'),
                    'response_id': result.get('id')
                }
            }
            
        except requests.exceptions.RequestException as e:
            print(f"\n=== AI API 请求异常 ===")
            print(f"异常类型: {type(e).__name__}")
            print(f"异常信息: {str(e)}")
            if hasattr(e, 'response') and e.response is not None:
                print(f"响应状态码: {e.response.status_code}")
                print(f"响应内容: {e.response.text}")
            print("=" * 50)
            raise AIServiceError(f"OpenAI API调用失败: {str(e)}")
        except (KeyError, IndexError) as e:
            raise AIServiceError(f"OpenAI API响应格式错误: {str(e)}")
    
    def _call_anthropic_api(
        self,
        messages: List[Dict[str, str]],
        provider: AIProvider,
        api_key: str
    ) -> Dict[str, Any]:
        """调用Anthropic API"""
        # TODO: 实现Anthropic API调用
        # 这里先返回模拟数据
        return {
            'content': "这是来自Anthropic的模拟回复。",
            'token_usage': {
                'input_tokens': 100,
                'output_tokens': 50,
                'total_tokens': 150
            },
            'metadata': {
                'model': self.conversation.ai_model.model_code,
                'finish_reason': 'end_turn'
            }
        }
    
    def _call_custom_api(
        self,
        messages: List[Dict[str, str]],
        provider: AIProvider,
        api_key: str
    ) -> Dict[str, Any]:
        """调用自定义API"""
        # TODO: 实现自定义API调用
        # 这里先返回模拟数据
        return {
            'content': "这是来自自定义API的模拟回复。",
            'token_usage': {
                'input_tokens': 80,
                'output_tokens': 40,
                'total_tokens': 120
            },
            'metadata': {
                'model': self.conversation.ai_model.model_code,
                'finish_reason': 'stop'
            }
        }
    
    def _log_token_usage(
        self,
        user: User,
        user_message: AIMessage,
        ai_message: AIMessage,
        token_usage: Dict[str, int]
    ):
        """记录Token使用情况"""
        TokenUsageLog.objects.create(
            user=user,
            conversation=self.conversation,
            message=ai_message,
            ai_model=self.conversation.ai_model,
            input_tokens=token_usage.get('input_tokens', 0),
            output_tokens=token_usage.get('output_tokens', 0),
            created_by=user
        )
    
    async def stream_chat(
        self,
        user_message_content: str,
        user: User,
        stream_callback: Callable[[Dict[str, Any]], None],
        include_context: bool = True,
        context_limit: int = 10,
        max_context_tokens: int = 120000,
        parent_message_id: Optional[int] = None
    ):
        """
        流式聊天API调用
        
        Args:
            user_message_content: 用户消息内容
            user: 发送消息的用户
            stream_callback: 流式数据回调函数
            include_context: 是否包含上下文
            context_limit: 上下文消息数量限制
            max_context_tokens: 上下文最大Token数
            parent_message_id: 父消息ID
        """
        if not self.conversation.ai_model:
            raise AIServiceError("未配置AI模型")
        
        # 1. 保存用户消息
        user_message = await self._async_save_user_message(
            content=user_message_content,
            user=user,
            parent_message_id=parent_message_id
        )
        
        # 2. 清除上下文缓存，确保新消息被包含
        await self._async_clear_context_cache()
        
        # 3. 构建AI请求上下文 - 使用滑动窗口
        if include_context:
            context_messages = await self._async_get_context_for_ai_request(
                message_limit=context_limit,
                token_limit=max_context_tokens
            )
        else:
            # 只包含系统提示词和当前消息
            context_messages = []
            if self.conversation.ai_role:
                context_messages.append({
                    'role': 'system',
                    'content': self.conversation.ai_role.system_prompt
                })
            context_messages.append({
                'role': 'user',
                'content': user_message_content
            })
        
        provider = self.conversation.ai_model.provider
        
        # 解密API密钥
        api_key = provider.decrypt_api_key()
        
        # 4. 根据提供商类型调用不同的流式API
        if provider.provider_type == 'openai':
            await self._stream_openai_api(context_messages, provider, api_key, stream_callback, user, user_message)
        elif provider.provider_type == 'anthropic':
            await self._stream_anthropic_api(context_messages, provider, api_key, stream_callback, user, user_message)
        else:
            await self._stream_custom_api(context_messages, provider, api_key, stream_callback, user, user_message)
    
    async def _stream_openai_api(
        self,
        messages: List[Dict[str, str]],
        provider: AIProvider,
        api_key: str,
        stream_callback: Callable[[Dict[str, Any]], None],
        user: User,
        user_message: AIMessage
    ):
        """流式调用OpenAI API"""
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json',
            'Accept': 'text/event-stream'
        }
        
        # 合并默认参数和模型参数
        params = provider.default_params.copy()
        params.update(self.conversation.ai_model.model_params)
        
        data = {
            'model': self.conversation.ai_model.model_code,
            'messages': messages,
            'stream': True,
            **params
        }
        
        # 处理URL拼接，避免双斜杠
        endpoint = provider.api_endpoint.rstrip('/')
        full_url = f"{endpoint}/chat/completions"
        
        print(f"\n=== 流式 AI API 请求详情 ===")
        print(f"URL: {full_url}")
        print(f"Headers: {headers}")
        print("Request Data:")
        print(json.dumps(data, indent=2, ensure_ascii=False))
        print("=" * 50)
        
        accumulated_content = ""
        total_tokens = 0
        
        try:
            timeout = aiohttp.ClientTimeout(total=300)  # 5分钟超时
            async with aiohttp.ClientSession(timeout=timeout) as session:
                async with session.post(full_url, headers=headers, json=data) as response:
                    if response.status != 200:
                        error_text = await response.text()
                        raise AIServiceError(f"OpenAI API错误 {response.status}: {error_text}")
                    
                    async for line in response.content:
                        line = line.decode('utf-8').strip()
                        
                        if not line or not line.startswith('data: '):
                            continue
                        
                        if line == 'data: [DONE]':
                            # 保存AI回复消息并更新上下文
                            ai_message = await self._async_save_ai_message(
                                content=accumulated_content,
                                user=user,
                                parent_message_id=user_message.id,
                                metadata={'total_tokens': total_tokens}
                            )
                            
                            # 更新上下文缓存
                            await self._async_add_message_to_context(user_message)
                            await self._async_add_message_to_context(ai_message)
                            
                            # 发送最终数据
                            await stream_callback({
                                'content': accumulated_content,
                                'tokens': total_tokens,
                                'is_final': True,
                                'ai_message_id': ai_message.id
                            })
                            break
                        
                        try:
                            # 解析JSON数据
                            json_data = line[6:]  # 移除 "data: " 前缀
                            chunk_data = json.loads(json_data)
                            
                            if 'choices' in chunk_data and len(chunk_data['choices']) > 0:
                                choice = chunk_data['choices'][0]
                                
                                if 'delta' in choice and 'content' in choice['delta']:
                                    content_chunk = choice['delta']['content']
                                    accumulated_content += content_chunk
                                    total_tokens += 1  # 粗略估算
                                    
                                    # 发送增量数据
                                    await stream_callback({
                                        'content': accumulated_content,
                                        'tokens': total_tokens,
                                        'is_final': False
                                    })
                        
                        except json.JSONDecodeError as e:
                            print(f"解析流式响应JSON失败: {e}, 原始数据: {line}")
                            continue
                        except Exception as e:
                            print(f"处理流式数据失败: {e}")
                            continue
        
        except Exception as e:
            print(f"\n=== 流式 AI API 请求异常 ===")
            print(f"异常类型: {type(e).__name__}")
            print(f"异常信息: {str(e)}")
            print("=" * 50)
            raise AIServiceError(f"OpenAI 流式API调用失败: {str(e)}")
    
    async def _stream_anthropic_api(
        self,
        messages: List[Dict[str, str]],
        provider: AIProvider,
        api_key: str,
        stream_callback: Callable[[Dict[str, Any]], None],
        user: User,
        user_message: AIMessage
    ):
        """流式调用Anthropic API"""
        # TODO: 实现Anthropic流式API调用
        # 这里先模拟流式输出
        mock_content = "这是来自Anthropic的模拟流式回复。我会逐字显示这个内容。"
        accumulated = ""
        
        for i, char in enumerate(mock_content):
            accumulated += char
            await asyncio.sleep(0.05)  # 模拟流式延迟
            
            await stream_callback({
                'content': accumulated,
                'tokens': i + 1,
                'is_final': i == len(mock_content) - 1
            })
    
    async def _stream_custom_api(
        self,
        messages: List[Dict[str, str]],
        provider: AIProvider,
        api_key: str,
        stream_callback: Callable[[Dict[str, Any]], None],
        user: User,
        user_message: AIMessage
    ):
        """流式调用自定义API"""
        # TODO: 实现自定义流式API调用
        # 这里先模拟流式输出
        mock_content = "这是来自自定义API的模拟流式回复。内容会逐步显示。"
        accumulated = ""
        
        for i, char in enumerate(mock_content):
            accumulated += char
            await asyncio.sleep(0.03)  # 模拟流式延迟
            
            await stream_callback({
                'content': accumulated,
                'tokens': i + 1,
                'is_final': i == len(mock_content) - 1
            })
    
    # 异步版本的数据库操作方法
    async def _async_save_user_message(
        self,
        content: str,
        user: User,
        parent_message_id: Optional[int] = None
    ) -> AIMessage:
        """异步保存用户消息"""
        from channels.db import database_sync_to_async
        
        @database_sync_to_async
        def save_user_message():
            return self._save_user_message(content, user, parent_message_id)
        
        return await save_user_message()
    
    async def _async_save_ai_message(
        self,
        content: str,
        user: User,
        parent_message_id: Optional[int] = None,
        metadata: Dict[str, Any] = None
    ) -> AIMessage:
        """异步保存AI回复消息"""
        from channels.db import database_sync_to_async
        
        @database_sync_to_async
        def save_ai_message():
            return self._save_ai_message(content, user, parent_message_id, metadata)
        
        return await save_ai_message()
    
    async def _async_clear_context_cache(self):
        """异步清除上下文缓存"""
        from channels.db import database_sync_to_async
        
        @database_sync_to_async
        def clear_cache():
            self.context_manager.clear_context_cache()
        
        await clear_cache()
    
    async def _async_get_context_for_ai_request(
        self,
        message_limit: int = 10,
        token_limit: int = 120000
    ) -> List[Dict[str, str]]:
        """异步获取用于AI请求的上下文格式"""
        from channels.db import database_sync_to_async
        
        @database_sync_to_async
        def get_context():
            return self.context_manager.get_context_for_ai_request(
                message_limit=message_limit,
                token_limit=token_limit
            )
        
        return await get_context()
    
    async def _async_add_message_to_context(self, message: AIMessage):
        """异步添加消息到上下文"""
        from channels.db import database_sync_to_async
        
        @database_sync_to_async
        def add_message():
            self.context_manager.add_message_to_context(message)
        
        await add_message()
